package com.leyou.item.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fasterxml.jackson.core.type.TypeReference;
import com.leyou.common.dto.PageDTO;
import com.leyou.common.exception.LyException;
import com.leyou.common.utils.JsonUtils;
import com.leyou.item.dto.CategoryDTO;
import com.leyou.item.dto.SpecParamDTO;
import com.leyou.item.dto.SpuDTO;
import com.leyou.item.dto.SpuDetailDTO;
import com.leyou.item.entity.Sku;
import com.leyou.item.entity.Spu;
import com.leyou.item.entity.SpuDetail;
import com.leyou.item.service.*;
import com.leyou.item.dto.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;


@Service
@Slf4j
public class GoodsServiceImpl implements GoodsService {

    private final SpuService spuService;
    private final SkuService skuService;
    private final SpuDetailService spuDetailService;

    private final BrandService brandService;
    private final CategoryService categoryService;
    private final SpecService specService;

    private final AmqpTemplate amqpTemplate;

    public GoodsServiceImpl(SpuService spuService,
                            SkuService skuService,
                            SpuDetailService spuDetailService,
                            BrandService brandService,
                            CategoryService categoryService,
                            SpecService specService,
                            AmqpTemplate amqpTemplate) {
        this.spuService = spuService;
        this.skuService = skuService;
        this.spuDetailService = spuDetailService;
        this.brandService = brandService;
        this.categoryService = categoryService;
        this.specService = specService;
        this.amqpTemplate = amqpTemplate;
    }

    @Override
    public PageDTO<SpuDTO> pageQuery(Integer rows, Integer page, Long brandId, Long categoryId, Boolean saleable, Long id) {
        IPage<Spu> iPage = new Page<>(page, rows);

        this.spuService.page(iPage,
                new QueryWrapper<Spu>()
                        .eq(null != brandId, "brand_id", brandId)
                        .eq(null != categoryId, "category_id", categoryId)
                        .eq(null != saleable, "saleable", saleable)
                        .eq(null != id, "id", id));

        List<SpuDTO> spuDTOS = SpuDTO.convertEntityList(iPage.getRecords());

        spuDTOS.forEach(spuDTO -> {
            String names = this.categoryService
                    .listCategoryByIds(spuDTO.getCategoryIds())
                    .stream()
                    .map(CategoryDTO::getName)
                    .collect(Collectors.joining("/"));
            spuDTO.setCategoryName(names);
            //TODO 获取到对象一定要保证非空,查询套在了循环中，有可能干死数据库，建议加缓存
            spuDTO.setBrandName(this.brandService.findBrandById(spuDTO.getBrandId()).getName());
        });
        return new PageDTO<SpuDTO>(iPage.getTotal(), iPage.getPages(), spuDTOS);
    }

    @Override
    public List<SkuDTO> listSkuBySqu(Long spuId) {

        return SkuDTO.convertEntityList(this.skuService.query().eq("spu_id", spuId).list());

    }

    @Override
    public List<SkuDTO> listSkuByIds(List<Long> ids) {

        return SkuDTO.convertEntityList(this.skuService.listByIds(ids));
    }

    @Override
    public SpuDetailDTO findSpuDetailBySpu(Long spuId) {

        SpuDetail spuDetail = this.spuDetailService.getById(spuId);
        if (null == spuDetail) {
            throw new LyException(400, "商品id有误，无所对应的商品详情信息");
        }
        return new SpuDetailDTO(spuDetail);
    }

    @Override
    public SpuDTO findSpuById(Long id) {
        return new SpuDTO(this.spuService.getById(id));
    }

    @Override
    public SpuDTO findGoodsById(Long id) {
        SpuDTO spuDTO = this.findSpuById(id);
        spuDTO.setSkus(this.listSkuBySqu(id));
        spuDTO.setSpuDetail(this.findSpuDetailBySpu(id));

        return spuDTO;
    }

    @Override
    public List<SpecParamDTO> findSpecParamWithValueBySpu(Long spuId, Boolean searching) {
        Long cid = this.findSpuById(spuId).getCid3();
        if (null == cid) {
            throw new LyException(400, "所提供的商品id有误");
        }
        List<SpecParamDTO> specParamDTOS = this.specService.listSpecParams(cid, searching, null);

        //获取到了所有的商品对应的规格参数，需要给规格参数赋值， json==>Map
        //paramMap的key是规格参数的id，specParamId,value是此规格参数的值，类型为Object
        Map<Long, Object> paramMap = JsonUtils.nativeRead(
                this.findSpuDetailBySpu(spuId).getSpecification(),
                new TypeReference<Map<Long, Object>>() {
                });

        specParamDTOS.forEach(specParamDTO -> specParamDTO.setValue(paramMap.get(specParamDTO.getId())));

        return specParamDTOS;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addGoods(SpuDTO spuDTO) {
        Spu spu = spuDTO.toEntity(Spu.class);

        //保存spu并主键回显
        this.spuService.save(spu);
        SpuDetail spuDetail = spuDTO.getSpuDetail().toEntity(SpuDetail.class);
        spuDetail.setSpuId(spu.getId());
        this.spuDetailService.save(spuDetail);

        List<SkuDTO> skuDTOS = spuDTO.getSkus();

        if (CollectionUtils.isEmpty(skuDTOS)) {
            throw new LyException(400, "至少应该包含一个sku");
        }

        //        List<Sku> skus = new ArrayList<>();
//        skusDTOs.forEach(skuDTO -> {
//            Sku sku = skuDTO.toEntity(Sku.class);
//            sku.setSaleable(true);
//            sku.setSpuId(spu.getId());
//           skus.add(sku) ;
//        });

        List<Sku> skusList = skuDTOS.stream().map(skuDTO -> {
            Sku sku = skuDTO.toEntity(Sku.class);
            sku.setSaleable(true);
            sku.setSpuId(spu.getId());
            return sku;
        }).collect(Collectors.toList());

        this.skuService.saveBatch(skusList);

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void modifySaleable(Long spuId, Boolean saleable) {
        Spu spu = new Spu();
        spu.setId(spuId);
        spu.setSaleable(saleable);
        //update tb_spu set saleable = #{saleable} where id = #{id}
        this.spuService.updateById(spu);

        Sku sku = new Sku();
        sku.setSaleable(saleable);
        //update tb_sku set saleable = #{saleable} where spu_id = #{spu_id}
        this.skuService.update(sku, new QueryWrapper<Sku>().eq("spu_id", spuId));

        //TODO 请求search服务，针对当前的商品做上架或者下架处理
        //feign

        //routingKey是队列和交换机绑定时给定匹配工具，只有生产者发送消息是，routingKey和交换机以及队列绑定时的key相同，此消息才能到达指定队列
        String routingKey = saleable ? "item.up" : "item.down";

        //消息发送，队列名称：jhj，routingKey负责匹配队列，spuId是实际发送的消息
        this.amqpTemplate.convertAndSend("jhj", routingKey, spuId);

        //TODO 1.feign请求page服务，删除对应spu的所有数据，2.不用变更java代码

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateGoods(SpuDTO spuDTO) {
        Long id = spuDTO.getId();

        //说明要修改spu
        if (null != id) {

            this.spuService.updateById(spuDTO.toEntity(Spu.class));
        }

        SpuDetailDTO spuDetailDTO = spuDTO.getSpuDetail();

        //说明要修改spuDetail
        if (null != spuDetailDTO) {
            this.spuDetailService.updateById(spuDetailDTO.toEntity(SpuDetail.class));
        }

        List<SkuDTO> skuDTOList = spuDTO.getSkus();

        //说明有sku要被修改,sku所谓的修改有三种可能性，分别为新增（id==null），修改(有id且saleable==null)，和删除(有id，且saleable==false)
        if (!org.springframework.util.CollectionUtils.isEmpty(skuDTOList)) {

            //分组结果map中总共两个元素一个为true:list<sku>,false:list<sku>
            Map<Boolean, List<Sku>> skuMap = skuDTOList
                    .stream()
                    .map(skuDTO -> skuDTO.toEntity(Sku.class))
                    .collect(Collectors.groupingBy(sku -> sku.getSaleable() == null));

            List<Sku> toBeAddOrUpdate = skuMap.get(true);

            if (!org.springframework.util.CollectionUtils.isEmpty(toBeAddOrUpdate)) {
                this.skuService.saveOrUpdateBatch(toBeAddOrUpdate);
            }


            List<Sku> toBeDelete = skuMap.get(false);

            if (!org.springframework.util.CollectionUtils.isEmpty(toBeDelete)) {
                //根据id删除
                this.skuService.removeByIds(toBeDelete.stream().map(Sku::getId).collect(Collectors.toList()));
            }


        }
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void minusStock(Map<Long, Integer> cartsMap) {

        //update tb_sku set stock = stock - #{num} where id = #{skuId}

        this.skuService.minusStock(cartsMap);

    }
}
